/*
 *  levelUpGame - GameView.cpp
 *  Copyright 2011 W. Reckman. All rights reserved.
 */
#include "Settings.h"
#include "LevelUpApp.h"
#include "Ghost.h"

#include "Pickup.h"
#include "GameView.h"

const float GameView::GAME_TIME_ALPHA = 0.85f;
const float GameView::TIMED_TEXT_FADEOUT = 0.5f;
const float GameView::FEEDBACK_BAD_MODERATE = 0.55f;
const float GameView::FEEDBACK_MODERATE_GOOD = 0.15f;
const float GameView::FEEDBACK_GOOD_BALANCE = 0.025f;


GameView::GameView( const LevelUpApp& appRef_ )
: s( Settings::getInstance() ), appRef( appRef_ ), game( appRef_.getGame() )
{}

void GameView::setup() {
	endSequenceAnimTime = ofToFloat( *s.getValueInGroup( "game_end_anim_time", "game", "0.5" ) );
	balanceDrawScale = ofToFloat( *s.getValueInGroup( "balance_draw_vscale", "screens", "1.0" ) );
}

void GameView::update() {
}

//FIXME: hardcoded offset values
void GameView::draw() {
	static const int textRowIndex = game.getRowLanes().size() - 1;
	
	const ViewController& vCtl = appRef.getViewController();
	const GameController& gCtl = appRef.getGameController();
	ofTrueTypeFont& smallFont = vCtl.getSmallFont();
	ofTrueTypeFont& mediumFont = vCtl.getMediumFont();
	ofTrueTypeFont& largeFont = vCtl.getLargeFont();
	ofTrueTypeFont& smallPixelFont = vCtl.getSmallPixelFont();
	ofTrueTypeFont& largePixelFont = vCtl.getLargePixelFont();
	const GameModel::vec_int& cl = game.getColumnLanes();
	const GameModel::vec_int& rl = game.getRowLanes();
	
	
	/* (debug) DRAW DEBUG */
	if ( appRef.debugDrawingEnabled ) {
		const ofRectangle& bb = game.getBoundingBox();
		int stageLaneBottomY = game.getStageLaneBottomY();
		
		ofPushStyle();
		
		ofSetLineWidth( 2.0f );
		
		ofSetColor( 255, 31, 31 );
		for ( int i = 0; i < rl.size(); ++i ) {
			ofLine( bb.x, rl[i], bb.x + bb.width, rl[i] );
		}
		
		for ( int i = 0; i < cl.size(); ++i ) {
			ofSetColor( 255, 31, 31 );
			ofLine( cl[i], bb.y, cl[i], bb.y + bb.height );
			ofSetColor( 31, 255, 31 );
			ofLine( cl[i], bb.y + bb.height, cl[i], stageLaneBottomY );
		}
		
		ofPopStyle();
	}
	
	
	/* DRAW PLAYERS */
	const GameModel::vec_PlayerP& players = game.getPlayers();
	for ( int i = 0; i < players.size(); ++i ) {
		if ( game.getState() != GS_END_SEQUENCE || i != game.getWinningPlayer() ) players[i]->draw();
	}
	
	/* DRAW PICKUPS */
	const GameModel::vec_PickupP& pickups = game.getPickups();
	for ( int i = 0; i < pickups.size(); ++i ) {
		const Pickup* pickup = pickups[i];
		pickup->draw();
	}
	
	/* DRAW GHOSTS */
	const Ghost* cg = game.getCultureGhost();
	const Ghost* ig = game.getIndustryGhost();
	if ( cg ) cg->draw();
	if ( ig ) ig->draw();
	
	
	/* (GS_PLAYING | GS_PRECOUNT | GS_END_SEQUENCE | GS_IDLE) DRAW CENTER TEXT (either 'waiting for players', countdown, start text or game time) */
	if ( game.getState() == GS_PLAYING ) {
		float timedTextLeft = game.getTimedTextLeft();
		const string& timedText = game.getTimedText();
		if ( timedTextLeft > 0.0f ) {
			float alpha = ( timedTextLeft > TIMED_TEXT_FADEOUT ) ? GAME_TIME_ALPHA : ( timedTextLeft / TIMED_TEXT_FADEOUT ) * GAME_TIME_ALPHA;
			ofSetColor( 255, 255, 255, 255 * alpha );
			drawTtfStringCentered( mediumFont, timedText, cl[cl.size() / 2], rl[textRowIndex] );
		} else {
			ofSetColor( 255, 255, 255, 255 * GAME_TIME_ALPHA );
			float timeLeft = game.getPlayTimeLeft();
			char timeText[100];
			snprintf( timeText, sizeof( timeText ), "%i:%.2i", (int)timeLeft / 60, (int)timeLeft % 60 );
			drawTtfStringCentered( smallFont, timeText, cl[cl.size() / 2], rl[textRowIndex] );
		}
		
	} else if ( game.getState() == GS_PRECOUNT ) {
		float pcTimeLeft = game.getPrecountTimeLeft();
		float alpha;
		alpha = 1.0f - modff( pcTimeLeft, &pcTimeLeft );
		
		ofSetColor( 255, 255, 255, 255 * alpha );
		const string& tls = ofToString( pcTimeLeft + 1, 0 );
		drawTtfStringCentered( mediumFont, tls, vCtl.getCanvasWidth() / 2 - 5, rl[textRowIndex] );
		
	} else if ( game.getState() == GS_END_SEQUENCE ) {
		
	} else if ( game.getState() == GS_IDLE ) {
		ofSetColor( 255, 255, 255, 255 * GAME_TIME_ALPHA );
		string idleText = "WAITING FOR PLAYERS";
		drawTtfStringCentered( mediumFont, idleText, cl[cl.size() / 2], rl[textRowIndex] );
	}
	ofSetColor( 255, 255, 255, 255 );
	
	
	/* ( ! GS_IDLE ) DRAW BALANCE METER */
	if ( game.getState() != GS_IDLE && players.size() == game.getMaxNumPlayers() ) {
		static const int afterColLane = 6;
		ViewController::vec_ofImage& tgtBalImgs = const_cast<ViewController::vec_ofImage&>( vCtl.getTgtBalanceImages() );
		ofRectangle meterBB; calcBBoxForWindow( meterBB, afterColLane, rl.size() - 1, balanceDrawScale );
		
		//TEMP test bbox for balance meter
		if ( appRef.debugDrawingEnabled ) {
			ofPushStyle(); ofNoFill(); ofSetLineWidth( 5 );
			ofRect( meterBB.x, meterBB.y, meterBB.width, meterBB.height );
			ofPopStyle();
		}
		
		float oneThird = meterBB.width / 3.0f;
		for ( int i = 0; i < players.size(); ++i ) {
			const Player* p = players[i];
			ofRectangle playerBB; calcBBoxForWindow( playerBB, p->getStartingLane() - 1, rl.size() - 1, balanceDrawScale );
			ofImage &bmImg = tgtBalImgs[i];
			
			//draw player target
			float pbY = ofMap( p->getTargetBalance().getBalanceFloat(), -0.5f, 0.5f, meterBB.y, meterBB.y + meterBB.height, true );
			bmImg.draw( meterBB.x + oneThird * i, pbY - bmImg.getHeight() / 2, oneThird, bmImg.getHeight() * ( oneThird / bmImg.getWidth() ) );
			
			//draw feedback image
			float score = fabsf( game.calcPlayerScore( i ) );
			//NOTE: const cast hack to circumvent ofImage's draw() function constness problem
			ofImage* fbImg = getFeedbackImage( i, score );
			if ( fbImg ) fbImg->draw( playerBB.x + 10, playerBB.y + 30 );
			
			//draw player number
			string numText = ofToString( i + 1 );
			int ntw = largePixelFont.stringWidth( numText );
			int ntx = playerBB.x + playerBB.width / 2 - ntw / 2, nty = playerBB.y + playerBB.height;
			const ofColor& pc = game.getPlayerColor( i, PCB_DIMMED );
			ofSetColor( pc.r, pc.g, pc.b ); largePixelFont.drawString( numText, ntx - 10, nty - 30 );
			ofSetColor( 255, 255, 255 );
		}
		
		//draw balance pointer
		ofImage& balPtrImg = const_cast<ofImage&>( vCtl.getBalancePointerImage() );
		float gby = ofMap( game.getBalanceMeter().getBalanceFloat(), -0.5f, 0.5f, meterBB.y, meterBB.y + meterBB.height, true );
		//balPtrImg.draw( meterBB.x, gby - balPtrImg.getHeight() / 2, meterBB.width, balPtrImg.getHeight() * ( meterBB.width / balPtrImg.getWidth() ) );
		//FIXME: quick hack to draw balance indicator with correct size
		balPtrImg.draw( meterBB.x, gby - balPtrImg.getHeight() / 2, meterBB.width * 1.7f, balPtrImg.getHeight() * ( meterBB.width * 1.7f / balPtrImg.getWidth() ) );
	}
	
	
	/* (GS_END_SEQUENCE) DRAW WINNING PLAYER */
	int wpid = game.getWinningPlayer();
#ifdef DRAW_WIN_MANY_HEADS
	if ( game.getState() == GS_END_SEQUENCE && wpid >= 0 ) {
		float ap = 1.0f - ( gCtl.getEndSequenceTimer() - game.getTime() ) / endSequenceAnimTime;
		for ( int ci = 0; ci < cl.size(); ++ci ) {
			for ( int ri = 0; ri < rl.size(); ++ri ) {
				float scale = ( ci == cl.size() / 2 && ri == rl.size() / 2 ) ? 4.0f : 1.0f;
				players[wpid]->drawAtPos( cl[ci], rl[ri], ap, scale );
			}
		}
		char winText[1000] = "";
		snprintf( winText, sizeof( winText ), "PLAYER %i WINS!", wpid + 1 );
		ofSetColor( 255, 255, 255, 255 );
		drawTtfStringCentered( mediumFont, winText, cl[cl.size() / 2], rl[textRowIndex] );
	}
#else
	if ( game.getState() == GS_END_SEQUENCE && gCtl.getEndSequenceStage() == 1 && wpid >= 0 ) {
		float ap = 1.0f - ( gCtl.getEndSequenceTimer() - game.getTime() ) / endSequenceAnimTime;
		players[wpid]->draw( true, ap );
	} else if ( game.getState() == GS_END_SEQUENCE && gCtl.getEndSequenceStage() == 2 && wpid >= 0 ) {
		players[wpid]->draw( true, 1.0f );
	}
#endif
	
	
	/* (debug) DRAW COLLISION+PAUSE INDICATORS */
	if ( appRef.debugDrawingEnabled ) {
		ofPushStyle();
		
		if ( game.p0Colliding ) {
			ofSetColor( 255, 0, 0 );
			ofCircle( 20, 20, 7 );
			ofSetColor( 255, 255, 255 );
		}
		if ( game.isPaused() ) {
			ofSetColor( 255, 255, 255 );
			ofDrawBitmapString( "P", 25, 10 );
		}
		
		ofPopStyle();
	}
}


/*********************
 * PRIVATE FUNCTIONS *
 *********************/

void GameView::calcBBoxForWindow( ofRectangle& bBox, int afterCol_, int afterRow_, float vScale_ ) const {
	const GameModel::vec_int& cl = game.getColumnLanes();
	const GameModel::vec_int& rl = game.getRowLanes();
	int hlw = game.getLaneWidth() / 2;
	int x = cl[afterCol_] + hlw, y = rl[afterRow_] + hlw;
	float w = ( afterCol_ < cl.size() - 1 ) ? cl[afterCol_ + 1] - hlw - x : 0;
	float h = ( afterRow_ < rl.size() - 1 ) ? rl[afterRow_ + 1] - y : game.getStageLaneBottomY() + hlw - y;
	float centerY = y + h / 2;
	float scaledH = h * vScale_;
	
	bBox.x = x;
	bBox.y = centerY - scaledH / 2;
	bBox.width = w;
	bBox.height = scaledH;
}

ofImage* GameView::getFeedbackImage( int playerIdx_, float score_ ) const {
	FEEDBACK_IMAGES fbType;
	
	if ( score_ >= FEEDBACK_BAD_MODERATE ) { fbType = FB_BAD; }
	else if ( score_ < FEEDBACK_BAD_MODERATE && score_ >= FEEDBACK_MODERATE_GOOD ) { fbType = FB_MODERATE; }
	else if ( score_ < FEEDBACK_MODERATE_GOOD && score_ >= FEEDBACK_GOOD_BALANCE ) { fbType = FB_GOOD; }
	else { fbType = FB_BALANCE; }
	
	return const_cast<ofImage*>( appRef.getViewController().getFeedbackImage( playerIdx_, fbType ) );
}
